export function patch(oldvnode, vnode) {
  if (!oldvnode) {
    return createELm(vnode);
  }
  //第一次渲染 判断是不是dom
  if (oldvnode?.nodeType === 1) {
    let el = createELm(vnode);
    //元素不是一样的 直接替换
    //替换 获取父节点 新的插入 删除老元素
    let parentEL = oldvnode.parentNode;
    parentEL.insertBefore(el, oldvnode.nextsibling); //插入某元素之后
    parentEL.removeChild(oldvnode);
    return el;
  } else {
    //元素不一样
    if (oldvnode === vnode) return;
    if (oldvnode.tag !== vnode.tag || oldvnode.key !== vnode.key) {
      oldvnode.el.parentNode.replaceChild(createELm(vnode), oldvnode.el);
      return;
    }
    //标签一样 处理文本 text 属性 <div>1</div> <div>2</div>
    if (!oldvnode.tag) {
      if (oldvnode.text !== vnode.text) {
        return (oldvnode.el.textContent = vnode.text);
      }
    }
    //标签一样 id 属性不一样
    //1.直接复制
    let el = (vnode.el = oldvnode.el);
    updataRrops(vnode, oldvnode.data);
    // 是否有子元素对比
    let oldChildren = oldvnode.children || [];
    let newChildren = vnode.children || [];

    if (oldChildren.length > 0 && newChildren.length > 0) {
      // 都有
      updataChild(oldChildren, newChildren, el);
    } else {
      el.innerHTML = "";
      for (let index = 0; index < newChildren.length; index++) {
        const child = newChildren[index];
        el.appendChild(createELm(child));
      }
    }
  }
}
//diff 算法
/** 
   *       odls      olde 
   * old      b    a   c  
   *       /     \    
   *      /       \
   *     /         \
   * new c   a   b  d
   *   onews        newe
   * dom b a c
   * ①依次比较，当比较成功后退出当前比较
     ②渲染结果以newVnode为准
     ③每次比较成功后start点和end点向中间靠拢
     ④当新旧节点中有一个start点跑到end点右侧时终止比较
     ⑤如果都匹配不到，则旧虚拟DOM key值去比对新虚拟DOM的key值，如果key相同则复用，并移动到新虚拟DOM的位置
  */
function updataChild(oldChildren, newChildren, parent) {
  //创建双指针
  let oldStartIndex = 0; //创建老dom开头的索引
  let oldStartNode = oldChildren[oldStartIndex];
  let oldEndIndex = oldChildren.length - 1; //创建老dom结尾的索引
  let oldEndNode = oldChildren[oldEndIndex];

  let newStartIndex = 0; //创建新dom开头的索引
  let newStartNode = newChildren[newStartIndex];
  let newEndIndex = newChildren.length - 1; //创建新dom结尾的索引
  let newEndNode = newChildren[newEndIndex];
  //创建旧元素的映射表
  function makeIndexBykey(children) {
    let map = {};
    children.forEach((child, index) => {
      //没有key
      if (child.key) {
        map[child.key] = index;
      }
    });
    return map;
  }
  let map = makeIndexBykey(oldChildren);
  function isSomeVnode(oldContext, newContext) {
    return (
      oldContext.tag === newContext.tag && oldContext.key === newContext.key
    );
  }
  while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {
    //比对子元素
    //头部比较 判断头部标签相同
    // 两个指针交叉对比
    if (isSomeVnode(oldStartNode, newStartNode)) {
      //递归
      patch(oldStartNode, newStartNode);
      //移动指针
      oldStartNode = oldChildren[++oldStartIndex];
      newStartNode = newChildren[++newStartIndex];
    } else if (isSomeVnode(oldEndNode, newEndNode)) {
      patch(oldEndNode, newEndNode);
      oldEndNode = oldChildren[--oldEndIndex];
      newEndNode = newChildren[--newEndIndex];
    } else if (isSomeVnode(oldStartNode, newEndNode)) {
      //老头 和 新旧对比 把比对好的新尾的节点插入到 老的（指针）节点的后面
      patch(oldStartNode, newEndNode);
      oldChildren[oldStartIndex] == null;
      parent.insertBefore(newEndNode.el, oldEndNode.el.nextSibling);
      oldStartNode = oldChildren[++oldStartIndex];
      newEndNode = newChildren[--newEndIndex];
    } else if (isSomeVnode(oldEndNode, newStartNode)) {
      //老尾 和 新头对比 把比对好的新头的节点插入到 老头的（指针）节点的前面
      patch(oldEndNode, newStartNode);
      oldChildren[oldEndIndex] = null;
      parent.insertBefore(newStartNode.el, oldStartNode.el);
      oldEndNode = oldChildren[--oldEndIndex];
      newStartNode = newChildren[++newStartIndex];
    } else {
      //没有任何关系时
      //从老的中寻找元素
      let moveIndex = map[newStartIndex.key];
      if (moveIndex == undefined) {
        //没有就添加到老的开头元素前面
        parent.insertBefore(createELm(newStartNode), oldStartNode.el);
      } else {
        let moveVnode = oldChildren[moveIndex]; //获取到老的对应的元素
        oldChildren[moveIndex] = null; //放置数组塌陷
        //插入               新的         旧的
        parent.insertBefore(moveVnode.el, oldStartNode.el);
        //moveVnode 可能有儿子 在去比较
        patch(moveVnode, newStartNode);
      }
      //新的元素唯一
      newStartNode = newChildren[++newStartIndex];
    }
  }
  //循环比对完成后 添加/删除 多余的项 把当前的元素以及后面的添加到dom上
  if (newStartIndex <= newEndIndex) {
    for (let index = newStartIndex; index <= newEndIndex; index++) {
      //添加
      parent.appendChild(createELm(newChildren[index]));
    }
  }
  //删除
  if (oldStartIndex <= oldEndIndex) {
    for (let index = oldStartIndex; index <= oldEndIndex; index++) {
      let child = oldChildren[index];
      if (child !== null) {
        parent.removeChild(child.el);
      }
    }
  }
  //为什么添加key不用索引？
  /**
   *  因为 key 遍历的时候是可以跟着子集走的，索引不是是跟着标签走的，所以当 diff 比较的时候找到相同的老旧索引但是里面子集不一样就会被重新创建
   * 如果用的是key那diff比较的时候就会找到两个相同的key里面的子集也是一样的只是位置不一样，这种情况所以还是key好。
   */
}
//添加属性
function updataRrops(vnode, oldvnode = {}) {
  //第一次
  let newProps = vnode.data || {};
  let el = vnode.el; //获取当时真实的dom
  //处理新旧的属性对比
  //1.老的有属性新的没有
  for (const key in oldvnode) {
    if (!newProps[key]) {
      el.removeAttribute(key); //删除属性
    }
  }
  //样式对比
  let newStyle = newProps.style || {}; //获取新的样式
  let oldStyle = oldvnode.style || {};
  for (const key in oldStyle) {
    //新的没有 老的有
    if (!newStyle[key]) {
      // el.style.[styleName]=""
      el.style = "";
    }
  }

  //初次渲染 新的有老的没有的
  for (const key in newProps) {
    if (key === "style") {
      for (const styleName in newProps.style) {
        el.style[styleName] = newProps.style[styleName];
      }
    } else if (key === "class") {
      el.className = newProps.class;
    } else {
      el.setAttribute(key, newProps[key]);
    }
  }
}
export function createELm(vnode) {
  let { vm, tag, children, key, data, text } = vnode;
  if (typeof tag === "string") {
    //组件
    if (createComponent(vnode)) {
      return vnode.componentInstance.$el;
    } else {
      //标签
      vnode.el = document.createElement(tag); //创建元素
      updataRrops(vnode);
      //子类
      if (children.length > 0) {
        children.forEach((child) => {
          vnode.el.appendChild(createELm(child));
        });
      }
    }
  } else {
    vnode.el = document.createTextNode(text);
  }
  return vnode.el;
}
function createComponent(vnode) {
  let i = vnode.data;
  if ((i = i.hook) && (i = i.init)) {
    i(vnode);
  }
  if (vnode.componentInstance) {
    return true;
  }
  return false;
}
